//
//  ZYWebImageManager.m
//  ZYWebImage
//
//  Created by wangzhipan on 2025/3/26.
//

#import "ZYWebImageManager.h"
#import "ZYWebImageCombinedTask.h"
#import "ZYWebImageConst.h"

@interface ZYWebImageManager()
@property (nonatomic, strong, nullable, readwrite) ZYImageCache *imageCache;
@property (nonatomic, strong, nullable, readwrite) ZYImageDownloader *downloader;
@property (nonatomic, strong, nullable) NSMutableArray *runningTasks;
@property (nonatomic, strong, nullable) NSLock *runningTasksLock;
@property (nonatomic, strong, nullable) NSMutableSet *failedURLs;
@property (nonatomic, strong, nullable) NSLock *failedURLsLock;
@end

@implementation ZYWebImageManager
+ (instancetype)sharedManager
{
    static id _instance;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _instance = [[self alloc] init];
    });
    return _instance;
}

- (instancetype)init
{
    if (self = [super init]) {
        self.imageCache = [ZYImageCache sharedImageCache];
        self.downloader = [ZYImageDownloader sharedInstance];
        self.runningTasks = [NSMutableArray array];
        self.runningTasksLock = [NSLock new];
        self.failedURLs = [NSMutableSet new];
        self.failedURLsLock = [NSLock new];
    }
    return self;
}

- (nullable ZYWebImageCombinedTask *)loadImageWithUrlStr:(NSString *)urlStr
                                         enableFailRetry:(BOOL)enableFailRetry
                                               completed:(ZYInternalCompletionBlock)completed
{
    if (!urlStr || !completed) {
        NSAssert(NO, @"urlStr and completed  can not nil");
    }
    NSURL *url = [NSURL URLWithString:urlStr];
    BOOL isFailUrl = NO;
    
    if (url) {
        [self.failedURLsLock lock];
        isFailUrl = [self.failedURLs containsObject:url];
        [self.failedURLsLock unlock];
    }
    //将cacheTask和downloadTask组合在一起
    ZYWebImageCombinedTask *task = [[ZYWebImageCombinedTask alloc] initWithManager:self];
    
    if (urlStr.length == 0 || (!enableFailRetry && isFailUrl)) {
        ZYWebImageError code = urlStr.length ? ZYWebImageErrorBlackList : ZYWebImageErrorInvalidURL;
        NSError *error = [NSError errorWithDomain:@"loadImageWithUrlStr error" code:code userInfo:nil];
        [self p_callCompletedBlockForTask:nil
                           completedBlock:completed
                                    image:nil
                                     data:nil
                                    error:error
                                cacheType:ZYImageCacheTypeNone
                                 finished:YES
                                      url:url];
        return task;
    }
    
    [self.runningTasksLock lock];
    [self.runningTasks addObject:task];
    [self.runningTasksLock unlock];
    
    [self p_callCacheProcessForOperation:task url:url completed:completed];
    return task;
}

- (void)cancelAll
{
    [self.runningTasksLock lock];
    NSArray <id<IZYTaskProtocol>> *tasks = [self.runningTasks copy];
    [self.runningTasksLock unlock];
    [tasks makeObjectsPerformSelector:@selector(cancel)];
}
- (void)removeFailedUrlStr:(NSString *)urlStr
{
    if (!urlStr) {
        return;
    }
    NSURL *url = [NSURL URLWithString:urlStr];
    [self.failedURLsLock lock];
    [self.failedURLs removeObject:url];
    [self.failedURLsLock unlock];
}
- (void)removeAllFailedUrls
{
    [self.failedURLsLock lock];
    [self.failedURLs removeAllObjects];
    [self.failedURLsLock unlock];
}

- (void)safeRemoveRunningTask:(nullable id<IZYTaskProtocol>)task 
{
    if (!task) {
        return;
    }
    [self.runningTasksLock lock];
    [self.runningTasks removeObject:task];
    [self.runningTasksLock unlock];
}

#pragma mark - private
- (void)p_callCompletedBlockForTask:(ZYWebImageCombinedTask *)task
                     completedBlock:(ZYInternalCompletionBlock)completedBlock 
                              image:(UIImage *)image
                               data:(NSData *)data
                              error:(NSError *)error
                          cacheType:(ZYImageCacheType)cacheType
                           finished:(BOOL)finished
                                url:(nullable NSURL *)url
{
    SafeAsyncMainThread(^{
        completedBlock(image,data,error,cacheType,finished,url);
    });
}

- (void)p_callCacheProcessForOperation:(nonnull ZYWebImageCombinedTask *)task
                                   url:(NSURL *)url
                             completed:(nullable ZYInternalCompletionBlock)completedBlock
{
    /*先询问缓存
     缓存没有再下载
     下载完存缓存
    */
    @weakify(self)
    @weakify(task)
    task.cacheTask = [self.imageCache queryCacheTaskForKey:url.absoluteString type:ZYImageCacheTypeAll completed:^(UIImage * _Nullable image, NSData * _Nullable data, ZYImageCacheType type) {
        @strongify(self)
        @strongify(task)
        if (!task || task.isCancelled) {
            NSError *error = [NSError errorWithDomain:@"Task cancelled by user during querying the cach" code:ZYWebImageErrorCancelled userInfo:nil];
            [self p_callCompletedBlockForTask:task
                               completedBlock:completedBlock
                                        image:nil
                                         data:nil
                                        error:error
                                    cacheType:ZYImageCacheTypeNone
                                     finished:YES
                                          url:url];
            [self safeRemoveRunningTask:task];
            return;
        }
        else if (!image) {
            [self p_callDownloadProcessForTask:task
                                           url:url
                                     completed:completedBlock];
            return;
        }
        [self p_callCompletedBlockForTask:task
                           completedBlock:completedBlock
                                    image:image
                                     data:data
                                    error:nil
                                cacheType:type
                                 finished:YES
                                      url:url];
        [self safeRemoveRunningTask:task];
    }];
}

- (void)p_callDownloadProcessForTask:(nonnull ZYWebImageCombinedTask *)task 
                                 url:(NSURL *)url
                           completed:(nullable ZYInternalCompletionBlock)completedBlock
{
    @synchronized (task) {
        task.cacheTask = nil;
    }
    @weakify(task)
    task.downloadTask = [self.downloader downloadImageWithUrlStr:url.absoluteString completed:^(UIImage * _Nullable image, NSData * _Nullable imageData, NSError * _Nullable error) {
        @strongify(task)
        if (!task || task.isCancelled) {
            NSError *error = [NSError errorWithDomain:@"Task cancelled by user during querying the cach" code:ZYWebImageErrorCancelled userInfo:nil];
            [self p_callCompletedBlockForTask:task
                               completedBlock:completedBlock
                                        image:nil
                                         data:nil
                                        error:error
                                    cacheType:ZYImageCacheTypeNone
                                     finished:YES
                                          url:url];
        }
        else if (error.code == ZYWebImageErrorCancelled) {  //用户手动cancel
            [self p_callCompletedBlockForTask:task
                               completedBlock:completedBlock
                                        image:nil
                                         data:nil
                                        error:error
                                    cacheType:ZYImageCacheTypeNone
                                     finished:YES
                                          url:url];
        }
        else if(error) {
            [self p_callCompletedBlockForTask:task
                               completedBlock:completedBlock
                                        image:nil
                                         data:nil
                                        error:error
                                    cacheType:ZYImageCacheTypeNone
                                     finished:YES
                                          url:url];
            [self.failedURLsLock lock];
            [self.failedURLs addObject:url];
            [self.failedURLsLock unlock];
        }
        else {
            [self.failedURLsLock lock];
            [self.failedURLs removeObject:url];
            [self.failedURLsLock unlock];
            
            [self p_callCompletedBlockForTask:task
                               completedBlock:completedBlock
                                        image:image
                                         data:imageData
                                        error:nil
                                    cacheType:ZYImageCacheTypeNone
                                     finished:YES
                                          url:url];
        }
        [self safeRemoveRunningTask:task];
    }];
}
@end
